﻿Imports System.Collections.Specialized
Imports System.Collections.ObjectModel
Namespace Collections
	<Serializable> Public Class CueList
        Inherits ObservableCollection(Of Cues.CueBase)

        ''' <summary>
        ''' Sorts the list by cue numbers, lowest to highest.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Sort()
			Dim Working As Boolean = True
			Do While Working = True
				Working = Sorter(Items.Count - 1)
			Loop
		End Sub

		''' <summary>
		''' Used by the sort function to sort the list by cue numbers, lowest to highest.
		''' </summary>
		''' <param name="MaxValue">The MaxValue to look at while sorting.</param>
		''' <returns></returns>
		''' <remarks></remarks>
		Private Function Sorter(MaxValue As Integer) As Boolean
			Dim DidWork As Boolean = False
			For i = 0 To MaxValue - 1
				If Item(i) > Item(i + 1) Then
					Move(i, i + 1)
					Sorter(i + 1)
					DidWork = True
				End If
			Next
			Return DidWork
		End Function

		''' <summary>
		''' Gets the list of cues between two cues.
		''' </summary>
		''' <param name="Cue1">The first cue. (Inclusive)</param>
		''' <param name="Cue2">The last cue. (Inclusive)</param>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Function GetViewBetween(Cue1 As Cues.CueBase, Cue2 As Cues.CueBase) As List(Of Cues.CueBase)
			Dim l As New List(Of Cues.CueBase)
			For i = IndexOf(Cue1) To IndexOf(Cue2)
				l.Add(Item(i))
			Next
			Return l
		End Function

		''' <summary>
		''' Returns true if the list contains a cue with the number provided.
		''' </summary>
		''' <param name="Value"></param>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Overloads Function Contains(Value As Double) As Boolean
			Return Me.Items.Where(Function(c) c.Number = Value).Count
		End Function

		''' <summary>
		''' Removes a cue from the list by the cue number provided.
		''' </summary>
		''' <param name="Value"></param>
		''' <remarks></remarks>
		Public Overloads Sub Remove(Value As Double)
			Remove(Me.Items.Where(Function(c) c.Number = Value)(0))
			My.Application.Show.ShowFile.IsSaved = False
		End Sub

		''' <summary>
		''' Returns true if there are any active cues in the list.
		''' </summary>
		''' <value></value>
		''' <returns></returns>
		''' <remarks></remarks>
		Public ReadOnly Property IsActive As Boolean
			Get
				If Me.Where(Function(c) c.IsActive = True).Count > 0 Then Return True Else Return False
			End Get
		End Property

        ''' <summary>
        ''' Returns a cue from the cue list.
        ''' </summary>
        ''' <param name="Number">The number of the cue to return.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function FindCueByNumber(Number As Double) As Cues.CueBase
            Try
                Return Items.Where(Function(c) c.Number = Number)(0)
            Catch ex As Exception
                Return Nothing
            End Try
        End Function

        ''' <summary>
        ''' Returns the number of selected items in the cue list. This number is determined by the GUI.
        ''' </summary>
        ''' <returns></returns>
        Public Function GetSelectedCueCount() As Integer
            Dim count As Integer = 0
            count = Items.Where(Function(c) c.IsSelected).Count

            For Each c In Items.OfType(Of Cues.Stack)
                count += c.CueList.GetSelectedCueCount
            Next

            Return count
        End Function

        ''' <summary>
        ''' Returns the first selected cue in the cue list.
        ''' </summary>
        ''' <returns></returns>
        Public Function GetSelectedCue() As Cues.CueBase
            Dim cue As Cues.CueBase
            cue = Items.Where(Function(c) c.IsSelected)(0)
            If cue Is Nothing Then
                For Each t In Items.OfType(Of Cues.Stack)
                    cue = t.CueList.GetSelectedCue
                    If cue IsNot Nothing Then Exit For
                Next
            End If
            Return cue
        End Function

        ''' <summary>
        ''' Returns the first selected cue in the cue list.
        ''' </summary>
        ''' <returns></returns>
        Public Function GetSelectedCues() As List(Of Cues.CueBase)
            Dim cl As New List(Of Cues.CueBase)
            cl.AddRange(Items.Where(Function(c) c.IsSelected))
            For Each t In Items.OfType(Of Cues.Stack)
                cl.AddRange(t.CueList.GetSelectedCues)
            Next
            Return cl
        End Function

        ''' <summary>
        ''' Handles adding a new cue to the list. If the cue has a number it checks to see if it will overwrite
        ''' an existing cue, otherwise it places it in the list with that number. If the number is 0 it
        ''' automatically sets the number to the largest whole number plus one.
        ''' </summary>
        ''' <param name="cue">The cue to add.</param>
        ''' <remarks></remarks>
        Public Overloads Sub AddCue(cue As Cues.CueBase)
            If cue.Number = 0 Then
                If Count > 0 Then
                    cue.Number = CType((Max.Number + 1).ToString.Split(".")(0), Double)
                Else
                    cue.Number = 1
                End If
                Add(cue)
                AddHandler cue.PropertyChanged, AddressOf My.Application.Show.ShowFile.ACueIsChanged
                My.Application.Show.ShowFile.IsSaved = False
            Else
                If Contains(cue) Then
                    If MsgBox("A cue with this number already exists. Do you want to increment this number by .1 and retry?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
                        cue.Number = CType((cue.Number + 1).ToString.Split(".")(0), Double)
                        Add(cue)
                        Return
                    Else
                        Return
                    End If
                Else
                    Add(cue)
                    AddHandler cue.PropertyChanged, AddressOf My.Application.Show.ShowFile.ACueIsChanged
                    My.Application.Show.ShowFile.IsSaved = False
                    Return
                End If
            End If
            Sort()
        End Sub

        Public Function GetAudioCues() As List(Of Cues.Audio)
            Dim acl As New List(Of Cues.Audio)

            For Each c As Cues.CueBase In Me.Items
                If c.GetType Is GetType(Cues.Audio) Then
                    acl.Add(c)
                ElseIf c.GetType Is GetType(Cues.Stack) Then
                    acl.AddRange(CType(c, Cues.Stack).CueList.GetAudioCues)
                End If
            Next
            Return acl

        End Function



#Region "Cue Interactions"

        ''' <summary>
        ''' Called to start all 'cued' cues in the list.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Go()
			Dim ql As New List(Of Cues.CueBase)
			ql.AddRange(Me.Where(Function(c) c.Status = Cues.CueStatus.Cued))
			For Each q In ql
				StartCue(q)
			Next
		End Sub

		''' <summary>
		''' Used by the Go command as well as transport actions to start a cue.
		''' </summary>
		''' <param name="q"></param>
		''' <remarks></remarks>
		Public Sub StartCue(q As Cues.CueBase)
			Try
				If q.FullNumber <> Max.FullNumber AndAlso Item(IndexOf(q) + 1) IsNot Nothing Then
					Dim q2 As Cues.CueBase = Item(IndexOf(q) + 1)
					If q2.Status = Cues.CueStatus.Standby Then
						q2.Status = Cues.CueStatus.Cued
					Else
						q.Start()
						Return
					End If
					If q.TransportAction = Cues.CueActions.AdvanceAtEnd Then
						Dim ActionHandler As Cues.CueBase.StartTransportActionEventHandler = Sub()
																								 My.Application.Dispatcher.Invoke(Sub() StartCue(q2))
																								 RemoveHandler q.StartTransportAction, ActionHandler
																							 End Sub
						AddHandler q.StartTransportAction, ActionHandler
						'AddHandler q.StartTransportAction, Sub() StartCue(q2)
						q.Start()
					ElseIf q.TransportAction = Cues.CueActions.Next Then
						q.Start()
						StartCue(q2)
					ElseIf q.TransportAction = Cues.CueActions.Pause Then
						Dim ActionHandler As Cues.CueBase.StartTransportActionEventHandler = Sub()
																								 My.Application.Dispatcher.Invoke(Sub() StartCue(q2))
																								 RemoveHandler q.StartTransportAction, ActionHandler
																							 End Sub
						AddHandler q.StartTransportAction, ActionHandler
						'AddHandler q.StartTransportAction, Sub() StartCue(q2)
						q.Start()
					Else
						q.Start()
					End If
				Else
					q.Start()
				End If
			Catch
			End Try
		End Sub

		''' <summary>
		''' Iterates through every cue in the list (and every stack cue's cue list as well) to try and find a cue.
		''' </summary>
		''' <param name="FullNumber">The FullNumber of the cue to find.</param>
		''' <returns></returns>
		''' <remarks></remarks>
		Public Function FindCueByFullNumber(FullNumber As String) As Cues.CueBase
			For Each q In Me
				If q.FullNumber = FullNumber Then Return q
			Next
			For Each q In Me.OfType(Of Cues.Stack)()
				Dim q2 As Cues.CueBase = Nothing
				q2 = q.CueList.FindCueByFullNumber(FullNumber)
				If q2 IsNot Nothing Then Return q2
			Next
			Return Nothing
		End Function

		''' <summary>
		''' Iterates through every cue in the list to find that cue's owning cue list.
		''' </summary>
		''' <param name="FullNumber">The FullNumber of the cue that we're trying to find the list of.</param>
		''' <returns>Returns a CueList if one is found, else it returns nothing.</returns>
		''' <remarks></remarks>
		Public Function FindCuesList(FullNumber As String) As CueList
			For Each q In Me
				If q.FullNumber = FullNumber Then Return Me
			Next
			For Each q In Me.OfType(Of Cues.Stack)()
				Dim cl As CueList = Nothing
				cl = q.CueList.FindCuesList(FullNumber)
				If cl IsNot Nothing Then Return cl
			Next
			Return Nothing
		End Function

		''' <summary>
		''' Stops all the currently active cues and clears any pending cue threads.
		''' </summary>
		''' <remarks></remarks>
		Public Async Function StopAllCues(Optional UseFade As Boolean = False) As Task
			Await Task.Run(Sub()
							   If Count > 0 Then
								   If Not UseFade Then
									   For Each cue As Cues.Audio In OfType(Of Cues.Audio)()
										   If cue.Player IsNot Nothing Then cue.Player.Mute()
									   Next
								   End If
								   For Each cue In Me
									   'Dim t As New System.Threading.Thread(Sub() cue.Stop(UseFade))
									   't.Start()
									   cue.Stop(UseFade)
								   Next
							   End If
						   End Sub)
			'ClearTransportActionThreads()
		End Function

		Public Async Function Reset() As Task
			Try
				My.Application.Show.Console.SendMessage("Attempting to reset the show.")
				Await StopAllCues()
				For Each cue In Me
					cue.Standby()
				Next
				System.Threading.Thread.Sleep(0)
				Item(0).Status = Cues.CueStatus.Cued
			Catch ex As Exception
				My.Application.Show.Console.SendMessage("The Cue List couldn't be reset.", ex)
			End Try
			System.Threading.Thread.Sleep(0)
			If Item(0).Status <> Cues.CueStatus.Cued Then
				Item(0).Status = Cues.CueStatus.Cued
			End If
		End Function

		<Obsolete("No longer functions", True)> Public Sub Go(cue As Cues.CueBase, Optional b As Boolean = False, Optional c As Boolean = False)
		End Sub
		<Obsolete("No longer functions", True)> Public Sub CueStart(cue As Cues.CueBase)
		End Sub


#End Region
	End Class
End Namespace